<?php
/**
 * Core_Role_AccessForm
 * 
 * @author naspeh
 * @version $Id: AccessForm.php 671 2008-06-30 12:06:16Z naspeh $
 */
class Core_Role_AccessForm extends Core_Form
{
    /**
     * Разделитель
     *
     */
    const ID_SEPARATOR = '_';
    /**
     * Обьект Acl 
     *
     * @var Core_Acl
     */
    protected $_acl = null;
    /**
     * Роль
     *
     * @var Core_Acl_Role
     */
    protected $_role = null;
    /**
     * Форма опций
     *
     * @var Core_Form
     */
    protected $_formCheckboxes = null;    
    /**
     * Инициализация
     *
     * @return Core_Role_AccessForm
     */
    public function init()
    {
        $this->build();
        $this->addSubForm($this->getFormCheckboxes(), 'resources');
        $this->addElement('button', 'submit_allow', array(
            'type'     => 'submit',
            'label'    => 'Разрешить',
            'attribs'  => array(
                'class' => 'positive',
                'icon'  => 'tick'
            )
        ));
        $this->addElement('button', 'submit_deny', array(
            'type'     => 'submit',
            'label'    => 'Запретить',
            'attribs'  => array(
                'class' => 'negative',
                'icon'  => 'cross'
            )
        ));
        return $this;
    }
    /**
     * Возвращает форму опций
     *
     * @return Core_Form
     */
    public function getFormCheckboxes()
    {
        if (is_null($this->_formCheckboxes)) {
            $form = new Core_Form(array('withHash' => false));
            $form->setDecorators(array(
               'decorators' => array(
                   'FormElements',
                    array('HtmlTag', array('tag' => 'div'))
               )
            ));
            $this->_formCheckboxes = $form; 
        }
        return $this->_formCheckboxes;
    }
    /**
     * Обновляет форму
     *
     * @return Core_Role_AccessForm
     */
    public function refresh()
    {
        $this->_acl = null;
        $this->_role = null;
        $this->build();
        return $this;
    }
    /**
     * Строит форму опций
     *
     * @return Core_Role_AccessForm
     */
    public function build()
    {
        $table = new Core_Table_Resources();
        $fields = array(
            'action_id' => array(
                'table'=> 'Core_Table_Actions',
                'resource' => 'Core_Acl_Resource_Action'
            ),
            'role_id' => array(
                'table'=> 'Core_Table_Roles',
                'resource' => 'Core_Acl_Resource_Role'
            ),
            'resource_type_id' => array(
                'table'=> 'Core_Table_ResourceTypes',
                'resource' => 'Core_Acl_Resource_Type'
            )
        );
        $acl = $this->getAcl();
        foreach ($table->fetchAll() as $row) {
            foreach ($fields as $field => $class) {
                if (null != $row->$field) {
                    $resource = $row->findDependentRowset($class['table'], 'Resource');
                    $resource = $resource->current();
                    $resource = new $class['resource']($resource);
                }
            }
            $acl->active($resource);
        }
        foreach ($acl->getAll() as $resourceId) {
            if (!$acl->hasParent($resourceId)) {
                $isAllow = $acl->isAllowed($this->getRole(), $resourceId);
                $classAllow = $isAllow ? 'allow' : 'deny';
                $options = array(
                    'label' => $acl->get($resourceId)->getDesc(),
                    'decorators' => array(
                        'ViewHelper',
                        array('Label', array('placement'=>'APPEND', 'class' => $classAllow)),
                        array('HtmlTag', array('tag' => 'div')),
                    )
                );
                $isAllow = $acl->isAllowed(Core_Auth::get('id'), $resourceId);
                if (!$isAllow) {
                    $options['attribs']['disable'] = 'disable';
                }
                $element = new Zend_Form_Element_Checkbox((string) $resourceId, $options);
                $this->getFormCheckboxes()->addElement($element); 
                $this->buildPrivileges($acl->get($resourceId));
                $children = 0;
                if ($acl->hasChildren($resourceId)) {
                    $children = $this->buildChildren($acl->getChildren($resourceId));
                }
                if (0 == $children && !$isAllow) {
                    $this->getFormCheckboxes()->removeElement($element->getName());
                }
            }
        }
        return $this;
    }
    /**
     * Строит детей
     *
     * @param array $resources
     * @param integer $step
     * @return integer
     */
    public function buildChildren($resources, $step = 1)
    {
        $count = 0;
        $acl = $this->getAcl();
        foreach ($resources as $resourceId) {
            $isAllow = $acl->isAllowed($this->getRole(), $resourceId);
            $classAllow = $isAllow ? 'allow' : 'deny';
            $class = 0 < $step ? 'prepend-' . $step : '';
            $options = array(
                'label' => $acl->get($resourceId)->getDesc(),
                'decorators' => array(
                    'ViewHelper',
                    array('Label', array('placement'=>'APPEND', 'class' => $classAllow)),
                    array('HtmlTag', array('tag' => 'div', 'class' => $class)),
                )
            );
            $isAllow = $acl->isAllowed(Core_Auth::get('id'), $resourceId); 
            if ($isAllow) {
                $count++;
            } else {
            	$options['attribs']['disable'] = 'disable';
            }
            $element = new Zend_Form_Element_Checkbox((string) $resourceId, $options);
            $this->getFormCheckboxes()->addElement($element);
            $this->buildPrivileges($acl->get($resourceId), $step);
            $children = 0;
            if ($acl->hasChildren($resourceId)) {
                $children = $this->buildChildren($acl->getChildren($resourceId), $step+1);
            }
            if (0 == $children && !$isAllow) {
                $this->getFormCheckboxes()->removeElement($element->getName());
            }
        }
        return $count;
    }
    /**
     * Строит привилегии
     *
     * @param Core_Acl_Resource_Abstract $resource
     * @param integer $step
     * @return integer
     */
    public function buildPrivileges(Core_Acl_Resource_Abstract $resource, $step = 0)
    {
        $count = 0;
        $privileges = $resource->getAllPrivileges();
        $prefixPrivilege = $resource->getResourceId()  . self::ID_SEPARATOR;
        $acl = $this->getAcl();
        foreach ($privileges as $privilege) {
            $isAllow = $acl->isAllowed($this->getRole(), $resource->getResourceId(), $privilege['id']);
            $class = 'prepend-' . ($step+1); 
            $classAllow = 'privilege ' . ($isAllow ? 'allow' : 'deny');
            $options = array(
                'label' => $privilege['desc'],
                'decorators' => array(
                        'ViewHelper',
                        array('Label', array('placement'=>'APPEND', 'class' => $classAllow)),
                        array('HtmlTag', array('tag' => 'div', 'class'=> $class)),
                )
            );
            if ($acl->isAllowed(Core_Auth::get('id'), $resource, $privilege['id'])) {
                $count++;
                $element = new Zend_Form_Element_Checkbox($prefixPrivilege . $privilege['id'], $options);
                $this->getFormCheckboxes()->addElement($element);
            } else {
            	$options['attribs']['disable'] = 'disable';
            }
        }
        return $count;
    }
    /**
     * Возвращает объект Acl
     *
     * @return Core_Acl
     */
    public function getAcl()
    {
         if (is_null($this->_acl)) {
            $this->_acl = new Core_Acl;
            $role = new Core_Acl_Role(Core_Auth::get());
            $this->_acl->activeRole($role);
         }
         return $this->_acl;
    }
    /**
     * Возвращает роль
     *
     * @return Core_Acl_Role
     */
    public function getRole()
    {
        if (false !== $this->getParams('role') && is_null($this->_role)) {
            $this->_role = new Core_Acl_Role($this->getParams('role'));
            $this->getAcl()->activeRole($this->_role);
        }
        return $this->_role;
    }
    /**
     * Запускает форму на выполнение
     *
     * @return Core_Acl_ActionsForm
     */    
    public function process()
    {
        $values = $this->getValues(true);
        if (!is_null($values['submit_allow'])) {
            $isAllow = true;
        } elseif (!is_null($values['submit_deny'])) {
            $isAllow = false;
        } else {
            return $this;
        }
        $values = $this->getFormCheckboxes()->getValues(true);
        $db = Core_Db::getInstance();
        $db->beginTransaction();
        foreach ($values as $key => $value) {
            $pattern = self::ID_SEPARATOR;
            if (preg_match('/(\w+)' . $pattern . '(\w+)$/', $key, $matches)) {
               $resourceId = $matches[1];
               $privilageId = $matches[2];
            } else {
               $resourceId = $key;
               $privilageId = null;  
            }
            if ($isAllow) {
                $method = 'allow';
            } else {
                $method = 'deny';
            }
            $acl = $this->getAcl();            
            if ($value && $acl->isAllowed(Core_Auth::get('id'), $resourceId, $privilageId)) {
                $acl->setIsUpadteDb(true)
                    ->$method($this->getRole(), $resourceId, $privilageId)
                    ->setIsUpadteDb(false);
                if (is_null($privilageId)) {
                	$this->processChildren($acl->get($resourceId));
                }
            }
        }
        $db->commit();
        Core_Message::add('Данные сохранены', Core_Message::TYPE_SUCCESS);
        $this->refresh();
        $this->setDefaults(array());
        return $this;
    }
    /**
     * Выполняет форму
     *
     * @param Core_Acl_Resource_Abstract $resource
     * @return Core_Role_AccessForm
     */
    public function processChildren(Core_Acl_Resource_Abstract $resource)
    {
        $acl = $this->getAcl();
        if (!$acl->isAllowed(Core_Auth::get('id'), $resource)) {
        	return $this;
        }
        $roleId = $this->getRole()->getRoleId();
        $tableRules = new Core_Table_Rules();
        $where = array(
            'role_id = ?' => $roleId, 
            'resource_id = ?' => $resource->getResourceId(),
            'privilege_id is not null'
        );
        $tableRules->delete($where);
        if ($acl->hasChildren($resource)) {
            $resources = $acl->getChildren($resource);
            $where = array(
                'resource_id in (?)' => array($resources), 
                'role_id = ?' => $roleId
            );
            $tableRules->delete($where);
            foreach ($resources as $resourceId) {
                $this->processChildren($acl->get($resourceId));
            }
        }
        return $this;
    }
}